﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using CRL.Core.Extension;
namespace CRL.Data.LambdaQuery
{
    public interface ICaseExpression<T> : IQuery
    {
        ICaseExpression<T> Case<TResult>(Expression<Func<T, TResult>> memberExpression);
        ICaseExpression<T> When(Expression<Func<T, bool>> expression);
        ICaseExpression2<T, TResult> When<TResult>(TResult value);
        ICaseExpression2<T, TResult> Then<TResult>(Expression<Func<T, TResult>> expression);
    }
    public interface ICaseExpression2<T,TResult> : IQuery
    {
        ICaseExpression2<T, TResult> When(Expression<Func<T, bool>> expression);
        ICaseExpression2<T, TResult> When(TResult value);
        ICaseExpression2<T, TResult> Then(Expression<Func<T, TResult>> expression);
        ICaseExpression2<T, TResult> Else(Expression<Func<T, TResult>> expression);
        TResult End();
    }

    public class CaseExpression<T> : ICaseExpression<T>
    {
        internal LambdaQueryBase baseQuery;
        StringBuilder sb = new StringBuilder("case ");
        string func;
        internal CaseExpression(LambdaQueryBase _baseQuery,string _func)
        {
            baseQuery = _baseQuery;
            func = _func;
        }
        public ICaseExpression<T> Case<TResult>(Expression<Func<T, TResult>> memberExpression)
        {
            var exp = baseQuery.GetSelectField(true, memberExpression.Body, false).GetQueryFieldString();
            var index = exp.IndexOf(" as ");
            if (index > 0)
            {
                exp = exp.Substring(0, index);
            }
            if (!exp.Contains("."))
            {
                var prefix = baseQuery.GetPrefix(typeof(T), memberExpression.Parameters[0].Name, false);
                exp = $"{prefix}{exp}";
            }
            sb.Append($"{exp} ");
            return this;
        }

        public ICaseExpression<T> When(Expression<Func<T, bool>> expression)
        {
            var exp = baseQuery.FormatExpression(expression.Body).SqlOut;
            sb.Append($"when {exp} ");
            return this;
        }
        public ICaseExpression2<T, TResult> When<TResult>(TResult value)
        {
            object value2 = "";
            if (value == null)
            {
                value2 = "null";
            }
            else
            {
                if (value.GetType().IsNumeric())
                {
                    value2 = value;
                }
                else
                {
                    value2 = $"'{value}'";
                }
            }
            sb.Append($"when {value2} ");
            return new CaseExpression2<T, TResult>(this);
        }

        public ICaseExpression2<T, TResult> Then<TResult>(Expression<Func<T, TResult>> expression)
        {
            var exp = baseQuery.FormatExpression(expression.Body).SqlOut;
            sb.Append($"then {exp} ");
            return new CaseExpression2<T, TResult>(this);
        }
        internal ICaseExpression<T> Else<TResult>(Expression<Func<T, TResult>> expression)
        {
            var exp = baseQuery.FormatExpression(expression.Body).SqlOut;
            sb.Append($"else {exp} ");
            return this;
        }
        bool ended;
        internal ICaseExpression<T> End()
        {
            if (!ended)
            {
                ended = true;
                sb.Append($"end ");
            }
            return this;
        }

        public string GetQuery(bool appendOrderBy = true)
        {
            if (!string.IsNullOrEmpty(func))
            {
                return $"{func}({sb})";
            }
            return sb.ToString();
        }

    }

    public class CaseExpression2<T,TResult> : ICaseExpression2<T, TResult>
    {
        CaseExpression<T> caseExp;
        internal CaseExpression2(CaseExpression<T> _caseExp)
        {
            caseExp = _caseExp;
        }

        public ICaseExpression2<T, TResult> When(Expression<Func<T, bool>> expression)
        {
            caseExp.When(expression);
            return this;
        }
        public ICaseExpression2<T, TResult> When(TResult value)
        {
            caseExp.When(value);
            return this;
        }

        public ICaseExpression2<T, TResult> Then(Expression<Func<T, TResult>> expression)
        {
            caseExp.Then(expression);
            return this;
        }
        public ICaseExpression2<T, TResult> Else(Expression<Func<T, TResult>> expression)
        {
            caseExp.Else(expression);
            return this;
        }
        public TResult End()
        {
            caseExp.End();
            return default(TResult);
        }
        public string GetQuery(bool appendOrderBy = true)
        {
            return caseExp.GetQuery();
        }
    }

}
